/*
This source file is part of the open source TripleGasm project
Copyright (C) 2010 Roma Kovalev

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

For the latest info, see http://code.google.com/p/triplegasm
You can contact the author(s) via e-mail: endiannesz@gmail.com
*/

#pragma once
#ifndef BASE_SPHERE_HPP
#define BASE_SPHERE_HPP

#include <Ogre.h>

namespace TripleGasm
{
  class Ball;

  /**Represents a sphere in the game world.
  This abstract class has all the functions and variables
  required to setup a sphere, derived classes should implement the
  Move method which describes how a sphere moves.*/
  class BaseSphere
  {
  protected:
    /**All the colours a sphere can have.
    */
    enum sphere_type {RED, BLUE, GREEN, WHITE, GRAY};
    /**Some directions mostly used with #mpAttachPoints.
    */
    enum direction {LEFT, RIGHT, BOTTOM, TOP, NONE};
    /**Internal enumerations required to construct a sphere.
    */
    enum {SPHERE_SEGS= 16, SPHERE_RINGS= 16,
      SPHERE_RADIUS= 3, DIAMETER= SPHERE_RADIUS*2};

    static const Ogre::Vector3 LEFT_POINT;
    static const Ogre::Vector3 RIGHT_POINT;
    static const Ogre::Vector3 BOTTOM_POINT;
    static const Ogre::Vector3 TOP_POINT;

    /**Maximum amount of spheres on each side of the screen.
    Exceeding this amount causes game over.
    */
    static const BYTE MAX_SPHERES= 5;
    /**The size of a square matrix.
    Since game over occurs if theres MAX_SPHERES on one side of the screen,
    the matrix should be twice as much + 1, +1 being the center.
    */
    static const BYTE MATRIX_SIZE= 2*MAX_SPHERES + 1;

    /**A matrix containing all spheres attached to the manipulator.
    This matrix represents a local coordinate system, where columns are
    represented as the "X" axis, and rows as the "Y" axis in Cartesian coordinates.
    The location
    of any spheres in this matrix is not altered by rotation of the
    SceneNodes, this is very useful for checking all the spheres in there
    initial state. Heres is a graphical representation of a local_matrix
    with MATRIX_SIZE= 5 (MAX_SPHERES = 2).
    [0,0]	[1,0]	[2,0]	[3,0]	[4,0]
    [0,1]	[1,1]	[2,1]	[3,1]	[4,1]
    [0,2]	[1,2]	[2,2]	[3,2]	[4,2]
    [0,3]	[1,3]	[2,3]	[3,3]	[4,3]
    [0,4]	[1,4]	[2,4]	[3,4]	[4,4]
    local_matrix[MAX_SPHERES][MAX_SPHERES] in world coordinates represents
    the point (0,0). Generally, the conversion between local_matrix
    coordinates and world coordinates is described as followed:
    local_matrix[MAX_SPHERES + X][MAX_SPHERES + Y]= (X*DIAMETER,Y*DIAMETER).
    Multiplying by DIAMETER is required, because
    the distance between two intersecting spheres of the same radius, is the
    diameter of the sphere.
    */
    typedef BaseSphere* local_matrix[MATRIX_SIZE][MATRIX_SIZE];

  public:
    enum query_flags {SPHERE= 1 << 5, OTHER= 1 << 0};

    bool isChecked; /**<Public member that is accessed when updating the graph.*/
    /**An array of SceneNodes on each side of the sphere.
    Every sphere has 4 attach points, on the left, top, right and bottom side.
    Each of these points is a SceneNode, another sphere can inherit a
    pointer to this SceneNode, thus, attaching itself to one of these points.
    Every SceneNode in this array is a child of this spheres SceneNode,
    all of the attached SceneNodes rotate when the parent rotates.*/
    Ogre::SceneNode* mpAttachPoints[4];

    /**A static function that creates a sphere mesh.
    @note
    This function has to be called before any spheres are created, and it is
    called only once.
    */
    static void CreateSphere();

    BaseSphere();
    ~BaseSphere();

    /**Checks if this sphere intersects with another sphere.
    */
    bool IsIntersection(BaseSphere* pSphere);

    Ogre::SceneNode* GetNode();

    sphere_type GetType();

    /**This function must be implemented by derived classes.
    */
    virtual void Move(const Ogre::FrameEvent& evt)= 0;

    /**Returns the closest attach point to another sphere.
    */
    BYTE ClosestAttachPoint(BaseSphere* pBall);

    /**Returns the nodes position relative to its parent.
    */
    virtual const Ogre::Vector3& GetPosition();

    /**Returns the nodes position relative to world coordinates.
    */
    virtual const Ogre::Vector3& GetDerivedPosition();

    /**Returns the opposite direction.
    If you pass direction::LEFT, it will return direction::RIGHT, etc.
    */
    direction Mirror(const direction DIRECTION);

    /**Returns a position altered by a direction.
    If you supply (pair(5,5), direction::LEFT), it will return pair(4,5),
    because in a zero based square matrix, the element to the left of
    pair(5,5) would be one column closer to zero. 
    @params
    A pointer to a sphere which has the altered position you want to return.
    @params
    A direction which will alter the position.
    */
    std::pair<BYTE, BYTE> MirrorPos(BaseSphere* pSphere, const direction DIRECTION);

    /**Returns this spheres position in a square matrix.
    See #mGraphPos.*/
    const std::pair<BYTE, BYTE>& GetGraphPos();

    /**Returns the SceneManager that created this spheres entity (mesh).
    */
    Ogre::SceneManager* GetSceneManager();

    /**Returns the spheres name.
    The spheres name is the same as the SceneNodes name, this function
    is mainly used for debug output.*/
    const Ogre::String& GetName();

    /**Destroys this sphere.
    @note
    This must be called before an instance of BaseSphere is out of scope
    or the pointer to a BaseSphere is deleted.
    */
    void Destroy();

    /**Returns a pointer to a BaseSphere which is stored in an attach point.
    */
    BaseSphere* GetAttachPointData(const direction NUM_POINT);
    static BaseSphere* GetAttachPointData(Ogre::SceneNode* pNode);

    /**Stores a pointer to a BaseSphere in an attach point.
    */
    void BindAttachPoints(const direction NUM_POINT, BaseSphere* pSphere);

    /**Sets a node for an attach point.
    @note
    This will request Ogre to delete the previous node, if there is one.
    */
    void SetAttachPoint(const direction NUM_POINT, Ogre::SceneNode* pNode);

    virtual void SetNodeData(BaseSphere* pSphere);

  protected:
    /**Position in a square matrix.
    Position in this matrix represents the spheres in game location before
    any rotation is applied. A sphere with mGraphPos= (MAX_SPHERES,MAX_SPHERES)
    would be located at (0,0) in world coordinates, because MAX_SPHERES 
    is the center of the matrix. See #local_matrix*/
    std::pair<BYTE, BYTE> mGraphPos;
    sphere_type mType; /**<The spheres type (colour).*/
    Ogre::SceneNode* mpNode; /**<A pivot associated with this sphere.*/
    Ogre::Entity* mpEnt; /**<The spheres mesh.*/
  };
}

#endif